////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Roman Ranzmaier (http://www.ranzmaier.at/contact.aspx).  All rights reserved.
// 
// Codeplex project: http://gadgeteernetworking.codeplex.com
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


using System;
using Microsoft.SPOT;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace Ranzmaier.NETMF.GadgeteerNetworking
{
    /// <summary>
    /// Fast and Easy UDPServer
    /// </summary>
    public class UdpServer : NetWorkServerBase
    {

        private int _numOfListener;
        private Thread _listenerThread;

        /// <summary>
        /// Initializes a new instance of the <see cref="UdpServer"/> class.
        /// </summary>
        /// <param name="port">The port.</param>
        /// <param name="numOfListener">The num of listener. (not in use)</param>
        /// <param name="timeout">The sockettimeout.</param>
        public UdpServer(int port, int numOfListener, int timeout)
            : base(NetworkServerProtocol.UDP, IPAddress.Any, port, timeout)
        {
            _numOfListener = numOfListener;
            CreateSocket();
        }

        #region Events
        #region BeginDataReceiveEvent
        /// <summary>
        /// DataReceiveEventArgs
        /// </summary>
        public class DataReceiveEventArgs
        {
            private byte[] _data;
            private EndPoint _sender;
            /// <summary>
            /// Number of bytes in the buffer
            /// </summary>
            public int Lenght { get; private set; }

            /// <summary>
            /// Initializes a new instance of the <see cref="DataReceiveEventArgs"/> class.
            /// </summary>
            /// <param name="data">The data.</param>
            /// <param name="lenght">The lenght.</param>
            /// <param name="sender">The sender.</param>
            public DataReceiveEventArgs(byte[] data, int lenght, EndPoint sender)
            {
                _data = data;
                _sender = sender;
                Lenght = lenght;
            }

            /// <summary>
            /// Gets the received data.
            /// </summary>
            public byte[] Data
            {
                get
                {
                    return _data;
                }
            }

            /// <summary>
            /// Send data back to the client
            /// </summary>
            /// <param name="response">The response.</param>
            public void Reply(byte[] response)
            {
                using (System.Net.Sockets.Socket s = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
                    s.SendTo(response, SocketFlags.None, _sender);
            }

        }
        #endregion

        /// <summary>
        /// DataReceivedEventHandler
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Gadgeteer.Modules.Module.NetworkModule.UDPServer.DataReceiveEventArgs"/> instance containing the event data.</param>
        public delegate void DataReceivedEventHandler(EndPoint sender, DataReceiveEventArgs e);
        /// <summary>
        /// Occurs when network data received.
        /// </summary>
        public event DataReceivedEventHandler DataReceived;

        /// <summary>
        /// Called when network data received.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="lenght">The lenght.</param>
        /// <param name="sender">The sender.</param>
        protected virtual void OnDataReceive(byte[] data, int lenght, EndPoint sender)
        {
            if (DataReceived != null)
                DataReceived(sender, new DataReceiveEventArgs(data, lenght, sender));

        }
        #endregion

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public override void Start()
        {
            if (!Started)
            {
                Started = true;
                _listenerThread = new Thread(new ThreadStart(ClientListener));
                _listenerThread.Start();
            }
        }
        /// <summary>
        /// Stops this instance.
        /// </summary>
        public override void Stop()
        {
            if (Started)
            {
                try
                {
                    _listenerThread.Abort();
                }
                catch (ThreadAbortException) { }

                _listenerThread = null;
                _socket.Close();
            }
        }

        private void ClientListener()
        {
            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, Port);
            _socket.Bind(remoteEndPoint);

            while (true)
            {
                if (_socket.Poll(-1, SelectMode.SelectRead))
                {
                    byte[] inBuf = new byte[_socket.Available];
                    int lenght = _socket.ReceiveFrom(inBuf, ref remoteEndPoint);

                    OnDataReceive(inBuf, lenght, remoteEndPoint);
                }
            }
        }
    }
}
